// 定义超时ID的类型，兼容帧动画和原生setTimeout的返回值
type TimeoutID = number | ReturnType<typeof requestAnimationFrame>;

// 存储所有活跃的超时任务
interface TimeoutTask {
    id: TimeoutID;
    callback: () => void;
    startTime: number;
    delay: number;
    frameRequest?: number;
}

// 用于存储和管理所有任务的映射表
const tasks = new Map<TimeoutID, TimeoutTask>();
const nextTaskId = 1;

// 检测浏览器是否支持requestAnimationFrame和cancelAnimationFrame
const supportsFrameAnimation =
    typeof window !== 'undefined' &&
    typeof window.requestAnimationFrame === 'function' &&
    typeof window.cancelAnimationFrame === 'function';

/**
 * 帧动画回调处理函数
 * 检查是否达到延迟时间，如果达到则执行回调，否则继续等待下一帧
 */
function handleFrame(taskId: TimeoutID) {
    const task = tasks.get(taskId);
    if (!task) return;

    const currentTime = Date.now();
    if (currentTime - task.startTime >= task.delay) {
        // 延迟时间已到，执行回调并清理任务
        task.callback();
        tasks.delete(taskId);
    } else {
        // 继续等待下一帧
        const newFrameRequest = requestAnimationFrame(() => handleFrame(taskId));
        if (task) {
            task.frameRequest = newFrameRequest;
        }
    }
}

/**
 * 使用帧动画模拟setTimeout
 * @param callback 延迟后执行的回调函数
 * @param delay 延迟时间(毫秒)，默认为0
 * @returns 超时任务ID，用于clearFrameTimeout
 */
export function frameTimeout(callback: () => void, delay = 0): TimeoutID {
    if (typeof callback !== 'function') {
        throw new TypeError('回调必须是一个函数');
    }

    const taskId: TimeoutID = nextTaskId + 1;

    if (supportsFrameAnimation) {
        // 使用帧动画实现
        const startTime = Date.now();
        const frameRequest = requestAnimationFrame(() => handleFrame(taskId));

        tasks.set(taskId, {
            id: taskId,
            callback,
            startTime,
            delay,
            frameRequest
        });

        return taskId;
    }
    // 不支持帧动画，回退到原生setTimeout
    const timeoutId = window.setTimeout(() => {
        callback();
        tasks.delete(timeoutId);
    }, delay) as TimeoutID;

    tasks.set(timeoutId, {
        id: timeoutId,
        callback,
        startTime: Date.now(),
        delay
    });

    return timeoutId;
}

/**
 * 清除由frameTimeout创建的超时任务
 * @param id 要清除的超时任务ID
 */
export function clearFrameTimeout(id: TimeoutID) {
    if (id === undefined || id === null) return;

    const task = tasks.get(id);
    if (!task) return;

    // 根据使用的实现方式来清除任务
    if (supportsFrameAnimation && task.frameRequest) {
        cancelAnimationFrame(task.frameRequest);
    } else {
        clearTimeout(id as number);
    }

    tasks.delete(id);
}
